/**
 * Copyright 2021 nicos AG
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 * @author Simon Petrac <spetrac@nicos-ag.com>
 * @contributor Jörg Langkau <jlangkau@nicos-ag.com>
 * @license Apache-2.0
 */

const
    md_renderer  = exports,
    util         = require('./util.js'),
    current_date = new Date();

md_renderer['FIR_STATIC_ids3c-co_label'] = function (param, config) {
    switch (config.language) {
        //case 'de':
        //    return 'IDS Katalog über Zertifizierungskriterien für Komponenten';
        case 'en':
        default:
            return 'IDS Certification Criteria Catalog for Components';
    }
}; // FIR_STATIC_ids3c-co_label

md_renderer['FIR_STATIC_ids3c-co_version'] = function (param, config) {
    const
        {data}     = param,
        {language} = config,
        ids3c_co   = util.ns.ids3c_co(''),
        versionArr = data.getObjects(ids3c_co, util.ns.owl('versionInfo')),
        version    = util.getLangStringFromLiterals(versionArr, language);
    return version || param.version;
}; // FIR_STATIC_ids3c-co_version

md_renderer['FIR_STATIC_disclaimer'] = function (param, config) {
    const
        ids3c_co_label   = md_renderer['FIR_STATIC_ids3c-co_label'](param, config),
        ids3c_co_version = md_renderer['FIR_STATIC_ids3c-co_version'](param, config),
        render_datetime  = current_date.toUTCString();

    // return `${ids3c_co_label}, version '${ids3c_co_version}', ${render_datetime}`;
    // REM: excluded datetime to reduce git updated on lesser changes
    return `${ids3c_co_label}, version '${ids3c_co_version}'`;
}; // FIR_STATIC_disclaimer

md_renderer['FIR_STATIC_footer'] = function (param, config) {
    const
        horizontal_line = '\n---\n',
        code_start      = '\n```text\n',
        code_end        = '\n```\n',
        disclaimer      = md_renderer['FIR_STATIC_disclaimer'](param, config);

    return horizontal_line
        + code_start + disclaimer + code_end
        + horizontal_line;
}; // FIR_STATIC_footer

md_renderer['FIR_STATIC_questionnaire_label'] = function (param, config) {
    switch (config.language) {
        //case 'de':
        //    return 'IDS Fragenkatalog zum Checklistenverfahren für Komponenten';
        case 'en':
        default:
            return 'IDS Questionnaire of Checklist Approach for Components';
    }
}; // FIR_STATIC_questionnaire_label

md_renderer['FIR_STATIC_questionnaire_version'] = function (param, config) {
    const
        {data}        = param,
        {language}    = config,
        questionnaire = util.ns.ids3c_co('IDS_CheckListApproach_Questionnaire'),
        versionArr    = data.getObjects(questionnaire, util.ns.owl('versionInfo')),
        version       = util.getLangStringFromLiterals(versionArr, language);
    return version || '';
}; // FIR_STATIC_questionnaire_version

md_renderer['FIR_STATIC_questionnaire_disclaimer'] = function (param, config) {
    const
        questionnaire_label   = md_renderer['FIR_STATIC_questionnaire_label'](param, config),
        questionnaire_version = md_renderer['FIR_STATIC_questionnaire_version'](param, config),
        render_datetime       = current_date.toUTCString();

    // return `${questionnaire_label}, version '${questionnaire_version}', ${render_datetime}`;
    // REM: excluded datetime to reduce git updated on lesser changes
    return `${questionnaire_label}, version '${questionnaire_version}'`;
}; // FIR_STATIC_questionnaire_disclaimer

md_renderer['FIR_STATIC_questionnaire_footer'] = function (param, config) {
    const
        horizontal_line          = '\n---\n',
        code_start               = '\n```text\n',
        code_end                 = '\n```\n',
        disclaimer               = md_renderer['FIR_STATIC_disclaimer'](param, config),
        questionnaire_disclaimer = md_renderer['FIR_STATIC_questionnaire_disclaimer'](param, config);

    return horizontal_line
        + code_start + questionnaire_disclaimer + code_end
        + code_start + disclaimer + code_end
        + horizontal_line;
}; // FIR_STATIC_questionnaire_footer

md_renderer['FIR_DYNAMIC_TABLE_criteria'] = function (param, config) {
    const
        {data, graph} = param,
        {language}    = config;

    //let result = `\n## Table of Certification Criterias\n\n`;

    let result = `| ID | Criteria Title | BSP | TSP | TPSP | BBP | AIBP | UCBP |`;
    result += `\n|:---|:---------------|:----|:----|:-----|:----|:-----|:-----|`;

    const
        ids3c_co                = util.ns.ids3c_co(''),
        ids3cm_profile          = util.ns.ids3cm('profile'),
        //criteriaGroupArray = data.getSubjects(util.ns.rdf('type'), util.ns.ids3cm('CertificationCriteriaGroup')).sort(util.compareNodes),
        [criteriaGroupListNode] = data.getObjects(ids3c_co, util.ns.ids3c_co('certificationCriteriaGroup')),
        criteriaGroupArray      = util.collectArrayFromListNodes(data, criteriaGroupListNode);

    for (let criteriaGroup of criteriaGroupArray) {
        const
            groupId       = criteriaGroup.value.replace(ids3c_co.value, ''),
            groupLabel    = util.getLangStringFromLiterals(data.getObjects(criteriaGroup, util.ns.rdfs('label')), language),
            criteriaArray = data.getObjects(criteriaGroup, util.ns.ids3cm('criteria')).sort(util.compareNodes);

        result += `\n| **${groupId}** | **${groupLabel}** |    |    |    |    |    |    |`;

        for (let criteria of criteriaArray) {
            const
                criteriaId    = criteria.value.replace(ids3c_co.value, ''),
                criteriaLabel = util.getLangStringFromLiterals(data.getObjects(criteria, util.ns.rdfs('label')), language),
                criteriaBSP   = data.has(criteria, ids3cm_profile, util.ns.ids3c_co('BaseSecurityProfile')) ? 'x' : '-',
                criteriaTSP   = data.has(criteria, ids3cm_profile, util.ns.ids3c_co('TrustSecurityProfile')) ? 'x' : '-',
                criteriaTPSP  = data.has(criteria, ids3cm_profile, util.ns.ids3c_co('TrustPlusSecurityProfile')) ? 'x' : '-',
                criteriaBBP   = data.has(criteria, ids3cm_profile, util.ns.ids3c_co('BasicBrokerProfile')) ? 'x' : '-',
                criteriaAIBP  = data.has(criteria, ids3cm_profile, util.ns.ids3c_co('AdvancedInformationBrokerProfile')) ? 'x' : '-',
                criteriaUCBP  = data.has(criteria, ids3cm_profile, util.ns.ids3c_co('UsageControlBrokerProfile')) ? 'x' : '-';

            result += `\n| [\`${criteriaId}\`](#${criteriaId}) | ${criteriaLabel} | ${criteriaBSP} | ${criteriaTSP} | ${criteriaTPSP} | ${criteriaBBP} | ${criteriaAIBP} | ${criteriaUCBP} |`;
        }
    }

    return result;
}; // FIR_DYNAMIC_TABLE_criteria

md_renderer['FIR_DYNAMIC_LIST_criteria'] = function (param, config) {
    const
        {data, graph} = param,
        {language}    = config;

    //let result = `\n## List of Certification Criterias\n`;
    let result = '';

    const
        ids3c_co                = util.ns.ids3c_co(''),
        ids3cm_profile          = util.ns.ids3cm('profile'),
        //criteriaGroupArray = data.getSubjects(util.ns.rdf('type'), util.ns.ids3cm('CertificationCriteriaGroup')).sort(util.compareNodes),
        [criteriaGroupListNode] = data.getObjects(ids3c_co, util.ns.ids3c_co('certificationCriteriaGroup')),
        criteriaGroupArray      = util.collectArrayFromListNodes(data, criteriaGroupListNode);

    for (let criteriaGroup of criteriaGroupArray) {
        const
            groupLabel    = util.getLangStringFromLiterals(data.getObjects(criteriaGroup, util.ns.rdfs('label')), language),
            criteriaArray = data.getObjects(criteriaGroup, util.ns.ids3cm('criteria')).sort(util.compareNodes);

        result += `\n### ${groupLabel}\n`;

        for (let criteria of criteriaArray) {
            const
                criteriaId          = criteria.value.replace(ids3c_co.value, ''),
                criteriaLabel       = util.getLangStringFromLiterals(data.getObjects(criteria, util.ns.rdfs('label')), language),
                criteriaDescription = util.getLangStringFromLiterals(data.getObjects(criteria, util.ns.dct('description')), language),
                criteriaBSP         = data.has(criteria, ids3cm_profile, util.ns.ids3c_co('BaseSecurityProfile')) ? 'x' : '-',
                criteriaTSP         = data.has(criteria, ids3cm_profile, util.ns.ids3c_co('TrustSecurityProfile')) ? 'x' : '-',
                criteriaTPSP        = data.has(criteria, ids3cm_profile, util.ns.ids3c_co('TrustPlusSecurityProfile')) ? 'x' : '-',
                criteriaBBP         = data.has(criteria, ids3cm_profile, util.ns.ids3c_co('BasicBrokerProfile')) ? 'x' : '-',
                criteriaAIBP        = data.has(criteria, ids3cm_profile, util.ns.ids3c_co('AdvancedInformationBrokerProfile')) ? 'x' : '-',
                criteriaUCBP        = data.has(criteria, ids3cm_profile, util.ns.ids3c_co('UsageControlBrokerProfile')) ? 'x' : '-';

            result += `\n#### <a name="${criteriaId}"></a>\`${criteriaId}\` ${criteriaLabel}\n`;
            result += `\n${util.textToMarkdown(criteriaDescription)}`;
            result += `\n- Base Security Profile: **${criteriaBSP}**`;
            result += `\n- Trust Security Profile: **${criteriaTSP}**`;
            result += `\n- Trust Plus Security Profile: **${criteriaTPSP}**`;
            result += `\n- Basic Broker Profile: **${criteriaBBP}**`;
            result += `\n- Advanced Information Broker Profile: **${criteriaAIBP}**`;
            result += `\n- Usage Control Broker Profile: **${criteriaUCBP}**`;
            result += `\n---\n`;
        }
    }

    return result;
}; // FIR_DYNAMIC_LIST_criteria

// REM: this function should produce the same output as FIR_DYNAMIC_LIST_criteria,
// but from the graph-map instead of the data-dataset (currently not tested)
md_renderer['FIR_DYNAMIC_LIST_criteria_from_json'] = function (param, config) {
    const
        {data, graph} = param,
        {language}    = config;

    //let result = `\n## List of Certification Criterias\n`;
    let result = '';

    const
        jsonNodeArray      = Array.from(graph.values()),
        criteriaGroupArray = jsonNodeArray
            .filter((node) => util.toArray(node['@type']).includes('ids3cm:CertificationCriteriaGroup'))
            .sort(util.compareNodes);

    for (let criteriaGroup of criteriaGroupArray) {
        const
            groupLabel    = util.getLangStringFromLiterals(criteriaGroup['rdfs:label'], language),
            criteriaArray = util.toArray(criteriaGroup['ids3cm:criteria']);

        result += `\n### ${groupLabel}\n`;

        for (let criteria of criteriaArray) {
            const
                criteriaId          = criteria['@id'].replace('ids3c-co:', ''),
                criteriaLabel       = util.getLangStringFromLiterals(criteria['rdfs:label'], language),
                criteriaDescription = util.getLangStringFromLiterals(criteria['dct:description'], language),
                criteriaProfiles    = util.toArray(criteria['ids3cm:profile']).map((node) => node['@id']),
                criteriaBSP         = criteriaProfiles.includes('ids3c-co:BaseSecurityProfile') ? 'x' : '-',
                criteriaTSP         = criteriaProfiles.includes('ids3c-co:TrustSecurityProfile') ? 'x' : '-',
                criteriaTPSP        = criteriaProfiles.includes('ids3c-co:TrustPlusSecurityProfile') ? 'x' : '-',
                criteriaBBP         = criteriaProfiles.includes('ids3c-co:BasicBrokerProfile') ? 'x' : '-',
                criteriaAIBP        = criteriaProfiles.includes('ids3c-co:AdvancedInformationBrokerProfile') ? 'x' : '-',
                criteriaUCBP        = criteriaProfiles.includes('ids3c-co:UsageControlBrokerProfile') ? 'x' : '-';

            result += `\n#### <a name="${criteriaId}"></a>\`${criteriaId}\` ${criteriaLabel}\n`;
            result += `\n${util.textToMarkdown(criteriaDescription)}`;
            result += `\n- Base Security Profile: **${criteriaBSP}**`;
            result += `\n- Trust Security Profile: **${criteriaTSP}**`;
            result += `\n- Trust Plus Security Profile: **${criteriaTPSP}**`;
            result += `\n- Basic Broker Profile: **${criteriaBBP}**`;
            result += `\n- Advanced Information Broker Profile: **${criteriaAIBP}**`;
            result += `\n- Usage Control Broker Profile: **${criteriaUCBP}**`;
            result += `\n---\n`;
        }
    }

    return result;
}; // FIR_DYNAMIC_LIST_criteria_from_json

md_renderer['FIR_DYNAMIC_TABLE_questionnaire'] = function (param, config) {
    const
        {data, graph} = param,
        {language}    = config;

    //let result = `\n## Table of Questions for Checklist Approach\n`;

    let result = `| Group | Label | Question | Answers |`;
    result += `\n|:------|:------|:---------|:-------|`;

    const
        questionnaire  = graph.get('ids3c-co:IDS_CheckListApproach_Questionnaire'),
        criteriaGroups = util.toArray(questionnaire['ids3cm:criteriaGroup']).sort(util.compareNodes);

    for (let criteriaGroup of criteriaGroups) {
        const
            groupLabel     = util.getLangStringFromLiterals(criteriaGroup['rdfs:label'], language),
            groupQuestions = util.toArray(criteriaGroup['ids3cm:question']).sort(util.compareNodes);

        result += `\n| __${groupLabel}__ |   |   |   |`;

        for (let question of groupQuestions) {
            const
                questionLabel         = util.getLangStringFromLiterals(question['rdfs:label'], language),
                questionDescription   = util.getLangStringFromLiterals(question['dct:description'], language),
                questionsType         = question['@type'].replace('ids3cm:CheckListQuestion_', ''),
                validChecklistChoices = util.toArray(question['ids3cm:validChoice']),
                checklistChoices      = [
                    ...util.toArray(question['ids3cm:choice']),
                    ...util.toArray(question['ids3cm:invalidChoice'])
                ].sort(util.compareNodes),
                matrixColumns         = util.toArray(question['ids3cm:matrixColumn']).sort(util.compareNodes);

            result += `\n|   | ${questionLabel} | ${util.textToMarkdown(questionDescription)} | ${questionsType} |`;

            for (let choice of validChecklistChoices) {
                const choiceLabel = util.getLangStringFromLiterals(choice['rdfs:label'], language)
                    || '_' + choice['@id'].replace('ids3c-co:', '') + '_';
                result += `\n|   |   |   | - **${choiceLabel}** |`;
            }

            for (let choice of checklistChoices) {
                const choiceLabel = util.getLangStringFromLiterals(choice['rdfs:label'], language)
                    || '_' + choice['@id'].replace('ids3c-co:', '') + '_';
                result += `\n|   |   |   | - ${choiceLabel} |`;
            }

            for (let column of matrixColumns) {
                const columnLabel = util.getLangStringFromLiterals(column['rdfs:label'], language)
                    || '_' + column['@id'].replace('ids3c-co:', '') + '_';
                result += `\n|   |   |   | - ${columnLabel} |`;
            }
        }
    }

    return result;
}; // FIR_DYNAMIC_TABLE_questionnaire

md_renderer['FIR_DYNAMIC_TABLE_questionnaire_groups'] = function (param, config) {
    const
        {data, graph} = param,
        {language}    = config,
        ids3c_co      = util.ns.ids3c_co(''),
        ids3cm        = util.ns.ids3cm('');

    function _collectNodesOrList(nodeArr) {
        nodeArr.sort(util.compareNodes);
        return util.collectArrayFromListNodes(data, nodeArr);
    } // _collectNodesOrList

    function _renderCriteriaGroup(criteriaGroup) {
        const
            criteriaGroupLabel       = util.getLangStringFromLiterals(data.getObjects(criteriaGroup, util.ns.rdfs('label')), language)
                || criteriaGroup.value.replace(ids3c_co.value, ''),
            criteriaGroupDescription = util.getLangStringFromLiterals(data.getObjects(criteriaGroup, util.ns.dct('description')), language),
            questionArr              = _collectNodesOrList(data.getObjects(criteriaGroup, util.ns.ids3cm('question'))),
            resultChunks             = [];

        resultChunks.push('### ' + criteriaGroupLabel);
        if (criteriaGroupDescription) resultChunks.push(util.textToMarkdown(criteriaGroupDescription));
        resultChunks.push(_renderQuestionTable(questionArr));

        return resultChunks.join('\n\n');
    } // _renderCriteriaGroup

    function _renderQuestionTable(questionArr) {
        const
            tableHeader  = '| Label | Question | Answers | Relevant If |',
            tableDevider = '|:---|:---|:---|:---|',
            tableRows    = questionArr.map(_renderQuestionRow);

        return [tableHeader, tableDevider, ...tableRows].join('\n');
    } // _renderQuestionRow

    function _renderQuestionRow(question) {
        const
            questionAnchor      = '<a name="' + question.value.replace(ids3c_co.value, '') + '"></a>',
            questionLabel       = util.getLangStringFromLiterals(data.getObjects(question, util.ns.rdfs('label')), language)
                || '_' + question.value.replace(ids3c_co.value, '') + '_',
            questionDescription = util.getLangStringFromLiterals(data.getObjects(question, util.ns.dct('description')), language),
            rowFields           = [
                questionAnchor + ' **' + questionLabel.replace(/-/g, '&#8209;') + '**',
                util.textToMarkdown(questionDescription),
                _renderColumnAnswers(question),
                _renderColumnRelevantIf(question)
            ];

        return '| ' + rowFields.join(' | ') + ' |';
    } // _renderQuestionRow

    function _renderColumnAnswers(question) {
        const
            [questionType]    = data.getObjects(question, util.ns.rdf('type')),
            questionTypeLabel = util.getLangStringFromLiterals(data.getObjects(questionType, util.ns.rdfs('label')), language)
                || questionType.value.replace(ids3cm.value + 'CheckListQuestion_', ''),
            validChoiceArr    = data.getObjects(question, util.ns.ids3cm('validChoice')).sort(util.compareNodes),
            invalidChoiceArr  = data.getObjects(question, util.ns.ids3cm('invalidChoice')).sort(util.compareNodes),
            matrixColumnArr   = data.getObjects(question, util.ns.ids3cm('matrixColumn')).sort(util.compareNodes),
            resultLines       = [
                '`' + questionTypeLabel + '`',
                ...validChoiceArr.map(choice => '- **' + (util.getLangStringFromLiterals(data.getObjects(choice, util.ns.rdfs('label')), language)
                    || '_' + choice.value.replace(ids3c_co.value, '') + '_') + '**'),
                ...invalidChoiceArr.map(choice => '- ' + (util.getLangStringFromLiterals(data.getObjects(choice, util.ns.rdfs('label')), language)
                    || '_' + choice.value.replace(ids3c_co.value, '') + '_')),
                ...matrixColumnArr.map(column => '- ' + (util.getLangStringFromLiterals(data.getObjects(column, util.ns.rdfs('label')), language)
                    || '_' + column.value.replace(ids3c_co.value, '') + '_'))
            ];

        return resultLines.join(' <br> ');
    } // _renderColumnAnswers

    function _renderColumnRelevantIf(question) {
        const
            relevantIfArr = data.getObjects(question, util.ns.ids3cm('relevantIf')).sort(util.compareNodes),
            resultLines   = [];

        for (let relevantIf of relevantIfArr) {
            const
                relevantIfLabel  = util.getLangStringFromLiterals(data.getObjects(relevantIf, util.ns.rdfs('label')), language)
                    || '_' + relevantIf.value.replace(ids3c_co.value, '') + '_',
                relevantQuestion = [
                    ...data.getSubjects(util.ns.ids3cm('validChoice'), relevantIf),
                    ...data.getSubjects(util.ns.ids3cm('invalidChoice'), relevantIf)
                ].find(node => !util.ns.ids3cm('CheckListQuestionChoice').equals(data.getObjects(node, util.ns.rdf('type'))[0]));

            if (!relevantQuestion) continue;

            const
                relevantQuestionLabel       = util.getLangStringFromLiterals(data.getObjects(relevantQuestion, util.ns.rdfs('label')), language)
                    || '_' + relevantQuestion.value.replace(ids3c_co.value, '') + '_',
                relevantQuestionAnchorTaret = '#' + relevantQuestion.value.replace(ids3c_co.value, '');

            resultLines.push('[' + relevantQuestionLabel.replace(/-/g, '&#8209;') + '](' + relevantQuestionAnchorTaret + ') <br> - ' + relevantIfLabel);
        }

        return resultLines.join(' <br> ');
    } // _renderColumnRelevantIf

    const
        questionnaire    = util.ns.ids3c_co('IDS_CheckListApproach_Questionnaire'),
        criteriaGroupArr = _collectNodesOrList(data.getObjects(questionnaire, util.ns.ids3cm('criteriaGroup')));

    return criteriaGroupArr.map(_renderCriteriaGroup).join('\n\n');
}; // FIR_DYNAMIC_TABLE_questionnaire_groups

md_renderer['FIR_DYNAMIC_TABLE_specification_test_case'] = function (param, config) {
    const
        {data, graph} = param,
        {language}    = config;

    //let result = `\n## Table of Test Case Specification\n`;

    let result = `| Id | Label | Description|`;
    result += `\n|:---|:------|:-----------|`;

    const
        ids3c_co = util.ns.ids3c_co(''),
        tcsArray = data.getSubjects(util.ns.rdf('type'), util.ns.ids3cm('ComponentTestCaseSpecification')).sort(util.compareNodes);

    for (let tcsNode of tcsArray) {
        const
            tcsId          = tcsNode.value.replace(ids3c_co.value, ''),
            tcsLabel       = util.getLangStringFromLiterals(data.getObjects(tcsNode, util.ns.rdfs('label')), language),
            tcsDescription = util.getLangStringFromLiterals(data.getObjects(tcsNode, util.ns.dct('description')), language);

        result += `\n| [\`${tcsId}\`](#${tcsId}) | ${tcsLabel} | ${util.textToMarkdown(tcsDescription)} |`;
    }

    return result;
}; // FIR_DYNAMIC_TABLE_specification_test_case

md_renderer['FIR_DYNAMIC_LIST_specification_test_case'] = function (param, config) {
    const
        {data, graph} = param,
        {language}    = config;

    //let result = `\n## List of Test Case Specification\n`;

    let result = '';

    const
        ids3c_co = util.ns.ids3c_co(''),
        tcsArray = data.getSubjects(util.ns.rdf('type'), util.ns.ids3cm('ComponentTestCaseSpecification')).sort(util.compareNodes);

    for (let tcsNode of tcsArray) {
        const
            tcsId          = tcsNode.value.replace(ids3c_co.value, ''),
            tcsLabel       = util.getLangStringFromLiterals(data.getObjects(tcsNode, util.ns.rdfs('label')), language),
            tcsDescription = util.getLangStringFromLiterals(data.getObjects(tcsNode, util.ns.dct('description')), language);

        result += `\n#### <a name="${tcsId}"></a>\`${tcsId}\` ${tcsLabel}\n`;
        //result += `\n${util.textToMarkdown(tcsDescription)}`;
        result += `\n[${util.textToMarkdown(tcsDescription)}](./specs/${tcsId}.md)`;

    } // for ()

    return result;
}; // FIR_DYNAMIC_LIST_specification_test_case

md_renderer['FIR_DYNAMIC_TABLE_specification_test_design'] = function (param, config) {
    const
        {data, graph} = param,
        {language}    = config;

    //let result = `\n## Table of Test Design Specification\n`;

    let result = `| ID | BSP_CA | BSP_CR | BSP_HAE | TSP_CA | TSP_CR | TSP_HAE | TPSP_CA | TPSP_CR | TPSP_HAE |`;
    result += `\n|:---|:-------|:-------|:--------|:-------|:-------|:--------|:--------|:--------|:---------|`;

    const
        ids3c_co                = util.ns.ids3c_co(''),
        rdf_type                = util.ns.rdf('type'),
        ids3cm_criteria         = util.ns.ids3cm('criteria'),
        ids3cm_CTDS             = util.ns.ids3cm('ComponentTestDesignSpecification'),
        //criteriaGroupArray = data.getSubjects(util.ns.rdf('type'), util.ns.ids3cm('CertificationCriteriaGroup')).sort(util.compareNodes),
        [criteriaGroupListNode] = data.getObjects(ids3c_co, util.ns.ids3c_co('certificationCriteriaGroup')),
        criteriaGroupArray      = util.collectArrayFromListNodes(data, criteriaGroupListNode),
        securityProfileClasses  = [
            'ids3cm:ComponentTestDesignSpecification_BaseSecurityProfile',
            'ids3cm:ComponentTestDesignSpecification_TrustSecurityProfile',
            'ids3cm:ComponentTestDesignSpecification_TrustPlusSecurityProfile'
        ],
        strategyTypeProperties  = [
            'ids3cm:testDesignSpecificStrategy_ChecklistApproach',
            'ids3cm:testDesignSpecificStrategy_ConceptReview',
            'ids3cm:testDesignSpecificStrategy_HighAssuranceEvaluation'
        ];

    for (let criteriaGroup of criteriaGroupArray) {
        const
            //groupId       = criteriaGroup.value.replace(ids3c_co.value, ''),
            //groupLabel    = util.getLangStringFromLiterals(data.getObjects(criteriaGroup, util.ns.rdfs('label')), language),
            criteriaArray = data.getObjects(criteriaGroup, util.ns.ids3cm('criteria')).sort(util.compareNodes);

        for (let criteria of criteriaArray) {
            const
                //criteriaId    = criteria.value.replace(ids3c_co.value, ''),
                //criteriaLabel = util.getLangStringFromLiterals(data.getObjects(criteria, util.ns.rdfs('label')), language),
                criteriaReferer = data.getSubjects(ids3cm_criteria, criteria),
                [criteriaTDS]   = criteriaReferer.filter((refererNode) => data.has(refererNode, rdf_type, ids3cm_CTDS));

            if (!criteriaTDS) continue;
            const
                tdsId          = criteriaTDS.value.replace(ids3c_co.value, ''),
                //tdsLabel       = util.getLangStringFromLiterals(data.getObjects(criteriaTDS, util.ns.rdfs('label')), language),
                tdsNode        = graph.get(criteriaTDS.value.replace(ids3c_co.value, 'ids3c-co:')),
                rowResultArray = new Array(9).fill(false);

            for (let tdsSecurityProfile of util.toArray(tdsNode['ids3cm:testDesignSecurityProfile'])) {
                const
                    spTypes          = util.toArray(tdsSecurityProfile['@type']),
                    colOffset        = 3 * securityProfileClasses.findIndex((spClass) => spTypes.includes(spClass)),
                    specificStrategy = tdsSecurityProfile['ids3cm:testDesignSpecificStrategy'];

                if (!specificStrategy) continue;
                strategyTypeProperties.forEach((property, index) => {
                    if (specificStrategy[property])
                        rowResultArray[colOffset + index] = true;
                });
            }

            result += `\n| [\`${tdsId}\`](#${tdsId}) |`;
            for (let i = 0; i < 9; i++) {
                result += ` ${rowResultArray[i] ? 'x' : '-'} |`;
            }
        }
    }

    return result;
}; // FIR_DYNAMIC_TABLE_specification_test_design

md_renderer['FIR_DYNAMIC_LIST_specification_test_design'] = function (param, config) {
    const
        {data, graph} = param,
        {language}    = config;

    //let result = `\n## List of Test Design Specification\n`;

    let result = '';

    const
        ids3c_co                = util.ns.ids3c_co(''),
        rdf_type                = util.ns.rdf('type'),
        ids3cm_criteria         = util.ns.ids3cm('criteria'),
        ids3cm_CTDS             = util.ns.ids3cm('ComponentTestDesignSpecification'),
        //criteriaGroupArray = data.getSubjects(util.ns.rdf('type'), util.ns.ids3cm('CertificationCriteriaGroup')).sort(util.compareNodes),
        [criteriaGroupListNode] = data.getObjects(ids3c_co, util.ns.ids3c_co('certificationCriteriaGroup')),
        criteriaGroupArray      = util.collectArrayFromListNodes(data, criteriaGroupListNode),
        securityProfileClasses  = [
            'ids3cm:ComponentTestDesignSpecification_BaseSecurityProfile',
            'ids3cm:ComponentTestDesignSpecification_TrustSecurityProfile',
            'ids3cm:ComponentTestDesignSpecification_TrustPlusSecurityProfile'
        ],
        strategyTypeProperties  = [
            'ids3cm:testDesignSpecificStrategy_ChecklistApproach',
            'ids3cm:testDesignSpecificStrategy_ConceptReview',
            'ids3cm:testDesignSpecificStrategy_HighAssuranceEvaluation'
        ];

    for (let criteriaGroup of criteriaGroupArray) {
        const
            //groupId       = criteriaGroup.value.replace(ids3c_co.value, ''),
            //groupLabel    = util.getLangStringFromLiterals(data.getObjects(criteriaGroup, util.ns.rdfs('label')), language),
            criteriaArray = data.getObjects(criteriaGroup, util.ns.ids3cm('criteria')).sort(util.compareNodes);

        for (let criteria of criteriaArray) {
            const
                //criteriaId    = criteria.value.replace(ids3c_co.value, ''),
                //criteriaLabel = util.getLangStringFromLiterals(data.getObjects(criteria, util.ns.rdfs('label')), language),
                criteriaReferer = data.getSubjects(ids3cm_criteria, criteria),
                [criteriaTDS]   = criteriaReferer.filter((refererNode) => data.has(refererNode, rdf_type, ids3cm_CTDS));

            if (!criteriaTDS) continue;
            const
                tdsId          = criteriaTDS.value.replace(ids3c_co.value, ''),
                tdsLabel       = util.getLangStringFromLiterals(data.getObjects(criteriaTDS, util.ns.rdfs('label')), language),
                tdsDescription = util.getLangStringFromLiterals(data.getObjects(criteriaTDS, util.ns.dct('description')), language)
            ;

            result += `\n#### <a name="${tdsId}"></a>\`${tdsId}\` ${tdsLabel}\n`;
            //result += `\n${util.textToMarkdown(tdsDescription)}`;
            result += `\n[${util.textToMarkdown(tdsDescription)}](./specs/${tdsId}.md)`;
        }
    } // for ()

    return result;
}; // FIR_DYNAMIC_LIST_specification_test_design

md_renderer['FIR_DYNAMIC_TABLE_security_level'] = function (param, config) {
    const
        {data, graph} = param,
        {language}    = config;

    //let result = `\n## Mapping of Security Level to Security Profile\n`;

    // REM: this method is based on character decomposition to find the security profile
    // => in the future this might/should change to make model data more speakable

    let result = `| Category | Base SP | Trust SP | Trust+ SP | Basic BP | Advanced Information BP | Usage Control BP |`;
    result += `\n|:---------|:--------|:---------|:----------|:---------|:------------------------|:-----------------|`;

    const
        iec                 = util.ns.iec(''),
        profileColumnArrays = [
            data.getObjects(util.ns.ids3c_co('BaseSecurityProfile'), util.ns.ids3cm('securityLevel')),
            data.getObjects(util.ns.ids3c_co('TrustSecurityProfile'), util.ns.ids3cm('securityLevel')),
            data.getObjects(util.ns.ids3c_co('TrustPlusSecurityProfile'), util.ns.ids3cm('securityLevel')),
            data.getObjects(util.ns.ids3c_co('BasicBrokerProfile'), util.ns.ids3cm('securityLevel')),
            data.getObjects(util.ns.ids3c_co('AdvancedInformationBrokerProfile'), util.ns.ids3cm('securityLevel')),
            data.getObjects(util.ns.ids3c_co('UsageControlBrokerProfile'), util.ns.ids3cm('securityLevel'))
        ],
        iecTagArray         = Array.from(new Set(
            profileColumnArrays
                .flat(1)
                .map((node) => node.value.replace(iec.value, '').replace(/-\w+$/, ''))
        ).values()).sort(util.compareNodes);

    for (let iecTag of iecTagArray) {
        result += `\n| ${iecTag} |`;
        for (let columnArray of profileColumnArrays) {
            const
                securityLevel    = columnArray.find(node => node.value.startsWith(iec.value + iecTag)),
                securityLevelTag = securityLevel.value.replace(iec.value + iecTag + '-', '');
            result += ` ${securityLevelTag} |`;
        }
    }

    return result;
}; // FIR_DYNAMIC_TABLE_security_level

md_renderer['FIR_DYNAMIC_TABLE_attacker_model'] = function (param, config) {
    const
        {data, graph} = param,
        {language}    = config;

    let result = `| Security Profile | Attacker | Means | Resources | Skills | Motivation |`;
    result += `\n|:-----------------|:---------|:------|:----------|:-------|:-----------|`;

    const
        securityProfileArray = [
            ...data.getSubjects(util.ns.rdf('type'), util.ns.ids3cm('SecurityProfile')),
            ...data.getSubjects(util.ns.rdf('type'), util.ns.ids3cm('BrokerSecurityProfile'))
        ];

    for (let securityProfile of securityProfileArray) {

        const
            securityProfileLabel = util.getLangStringFromLiterals(data.getObjects(securityProfile, util.ns.rdfs('label')), language),
            [sp_attacker]        = data.getObjects(securityProfile, util.ns.ids3cm('sp_attacker')),
            [sp_means]           = data.getObjects(securityProfile, util.ns.ids3cm('sp_means')),
            [sp_resources]       = data.getObjects(securityProfile, util.ns.ids3cm('sp_resources')),
            [sp_skills]          = data.getObjects(securityProfile, util.ns.ids3cm('sp_skills')),
            [sp_motivation]      = data.getObjects(securityProfile, util.ns.ids3cm('sp_motivation'));

        if (!(sp_attacker || sp_means || sp_resources || sp_skills || sp_motivation)) continue;

        const
            attackerLabel   = sp_attacker && util.getLangStringFromLiterals(data.getObjects(sp_attacker, util.ns.rdfs('label')), language) || '',
            meansLabel      = sp_attacker && util.getLangStringFromLiterals(data.getObjects(sp_means, util.ns.rdfs('label')), language) || '',
            resourcesLabel  = sp_attacker && util.getLangStringFromLiterals(data.getObjects(sp_resources, util.ns.rdfs('label')), language) || '',
            skillsLabel     = sp_attacker && util.getLangStringFromLiterals(data.getObjects(sp_skills, util.ns.rdfs('label')), language) || '',
            motivationLabel = sp_attacker && util.getLangStringFromLiterals(data.getObjects(sp_motivation, util.ns.rdfs('label')), language) || '';

        result += `\n| ${securityProfileLabel} | ${attackerLabel} | ${meansLabel} | ${resourcesLabel} | ${skillsLabel} | ${motivationLabel} |`;
    }

    return result;
}; // FIR_DYNAMIC_TABLE_attacker_model
